package com.Util;


import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author :v-zhousq04
 * @description: 实体生成工具
 * @create date: 2018/9/4 10:42
 */
public class GeneratorUtil {

    static String templatePath="/template";
    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String setAndGetConsolText(String commentText) throws Exception {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + commentText + "：");
        System.out.println(help);
        if (scanner.hasNext()) {
            String inputStr = scanner.next();
            if (null!=inputStr&&!inputStr.isEmpty()) {
                return inputStr;
            }
        }
        throw new Exception("请输入正确的" + commentText + "！");
    }

    public static void generateCode()  {
        try {
            System.out.println("---------------welcome use code generator---------------");
            System.out.println("1.mybatis 代码模板  2.mybatis-plus 代码模板 3.其他模板 4.通过配置生成mybatis代码 5.通过配置生成mybatis-plus代码");
            System.out.println("快速生成默认使用配置文件中的作者,前缀,表名,表描述");
            System.out.println("---------------welcome use code generator---------------");
            String inputResult = setAndGetConsolText("代码模板");
            if (inputResult.equals(ConstantCode.NUMBER_ONE)||inputResult.equals(ConstantCode.NUMBER_FOUR)){
                templatePath=ConstantCode.MYBATIS_TEMPLATE_PATH;
            }else if(inputResult.equals(ConstantCode.NUMBER_TWO)||inputResult.equals(ConstantCode.NUMBER_FIVE)){
                templatePath=ConstantCode.MYBATIS_PLUS_TEMPLATE_PATH;
            } else if(inputResult.equals(ConstantCode.NUMBER_THREE)){
                templatePath=ConstantCode.OTHER_TEMPLATE_PATH;
            } else{
                System.exit(0);
            }
            EntityDataVO entityDataVO=new EntityDataVO();
//          初始化配置
            initBaseConfig(entityDataVO);
//          初始化数据类型映射
            initDataTypeMapping();
            String tableNames ,desc;
            if (inputResult.equals(ConstantCode.NUMBER_ONE)||inputResult.equals(ConstantCode.NUMBER_TWO)){
                String author= setAndGetConsolText("作者");
                entityDataVO.setAuthor(author);

                String tablPrefix = setAndGetConsolText("表前缀（没有输入1）");
                if (!tablPrefix.equals(ConstantCode.NUMBER_ONE)){
                    entityDataVO.setPrefix(tablPrefix);
                }
                tableNames = setAndGetConsolText("表名,多个表以逗号分开");
                desc = setAndGetConsolText("实体描述，多个表描述以逗号分开");
            }else {
                tableNames = entityDataVO.getTableName();
                desc = entityDataVO.getDesc();
            }
            String[] descArr = desc.split(",");
            String[] tabArr = tableNames.split(",");
            if (descArr.length!=tabArr.length){
                throw new Exception("表的描述量与表数量不匹配");
            }

            for (int i = 0; i < tabArr.length; i++) {
                entityDataVO.setDesc(descArr[i]);
                entityDataVO.setTableName(tabArr[i]);
                generateEntity(entityDataVO);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static void initDataTypeMapping() {
        //INT_TYPE
        ColumnMapping columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.INT_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.INT_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.INT_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.INT_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        //BIGINT_TYPE
        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.BIGINT_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.BIGINT_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.BIGINT_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.BIGINT_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        //BOOLEAN
        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.BOOLEAN_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.BOOLEAN_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.BOOLEAN_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.BOOLEAN_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.FLOAT_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.FLOAT_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.FLOAT_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.FLOAT_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.DOUBLE_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.DOUBLE_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.DOUBLE_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.DOUBLE_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        //DATE
        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.DATE_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.DATE_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.DATE_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.DATE_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        //TIME
        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.TIME_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.TIME_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.TIME_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.TIME_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        //DATETIME
        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.DATETIME_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.DATETIME_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.DATETIME_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.DATETIME_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.STRING_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.STRING_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.STRING_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.STRING_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.JSON_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.JSON_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.JSON_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.JSON_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);

        columnMapping=new ColumnMapping();
        columnMapping.setDbFieldTypeList(Arrays.asList(ConstantCode.DECIMAL_TYPE_ARR));
        columnMapping.setJavaType(ConstantCode.DECIMAL_TYPE);
        columnMapping.setJavaTypeFull(ConstantCode.DECIMAL_TYPE_FULL);
        columnMapping.setColumnJdbcType(ConstantCode.DECIMAL_TYPE_JDBC_TYPE_MAPPING);
        ConstantCode.mappingList.add(columnMapping);
    }

    /**
     * 生成文件
     * @param entityDataVO
     * @throws Exception
     */
    public static void generateEntity(EntityDataVO entityDataVO) throws Exception {
//            校验参数
        if (!TabUtil.valideParam(entityDataVO)) {
            return;
        }
        String tableName=entityDataVO.getTableName();
//            将下划线格式转换驼峰命名
        String renameTable = TabUtil.renameCol(tableName);
//            转换首字母大写
        String entityName = TabUtil.upperFirstFieldName(renameTable);
        entityDataVO.setCreateDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        entityDataVO.setEntityName(entityName);
        entityDataVO.setEntityNameLower(renameTable);

        TabUtil.setTableInfo(entityDataVO,tableName);
        if (entityDataVO.getColumnEntityList()==null){
            throw new Exception("数据库操作失败,请检查表参数");
        }
        try {
            String templateFile, fileName;
            templateFile = "entityTemplate.ftl";
            fileName = entityName +entityDataVO.getEntitySuffix()+ ".java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

           /* templateFile = "entityTemplateCriteria.ftl";
            fileName = entityName + "Criteria.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());*/

            templateFile = "voTemplate.ftl";
            fileName = entityName + "VO.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "dtoTemplate.ftl";
            fileName = entityName + "DTO.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "pageDtoTemplate.ftl";
            fileName = entityName + "PageDTO.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "daoTemplate.ftl";
            fileName = entityName + "Dao.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "mapperInterfaceTemplate.ftl";
            fileName = entityName + "Mapper.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "mapperXmlTemplate.ftl";
            fileName = entityName + "Mapper.xml";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "controllerTemplate.ftl";
            fileName = entityName + "Controller.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "serviceTemplate.ftl";
            fileName = entityName + "Service.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());

            templateFile = "serviceImplTemplate.ftl";
            fileName = entityName + "ServiceImpl.java";
            TabUtil.generateFile(entityDataVO, templateFile, fileName, entityDataVO.getTargetPath());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("数据操作异常");
        } catch (TemplateException e) {
            e.printStackTrace();
            System.out.println("！！！模板有误！！！");
        }
    }

    /**
     * 读取当前文件下的配置文件进行数据初始化
     * @param entityDataVO
     * @throws IOException
     */
    public static void initBaseConfig(EntityDataVO entityDataVO) throws Exception {
        try {
            String filePath = "/generator.properties";
            Properties prop = new Properties();
            InputStream inputStream = GeneratorUtil.class.getResourceAsStream(filePath);
            prop.load(new InputStreamReader(inputStream, "utf-8"));

            entityDataVO.setLombokEnable(Boolean.valueOf(checkProperty(prop, "gen.lombokEnable")));
            String basePkg = checkProperty(prop, "gen.basePkg");
            entityDataVO.setBasePkg(basePkg);

            entityDataVO.setEntityPkg(checkProperty(prop,"gen.entity.package"));
            String suffix= prop.getProperty("gen.entity.suffix");
            if (!suffix.isEmpty()){
                entityDataVO.setEntitySuffix(suffix);
            }else {
                entityDataVO.setEntitySuffix("");
            }
            String localDateEnable= prop.getProperty("gen.entity.localDateEnable");
            if (!localDateEnable.isEmpty()){
                entityDataVO.setLombokEnable(Boolean.valueOf(localDateEnable));
            }

            entityDataVO.setMapperPkg(checkProperty(prop,"gen.entity.mapper.package"));
            entityDataVO.setAuthor(checkProperty(prop,"gen.author"));
            entityDataVO.setDesc(checkProperty(prop,"gen.desc"));
            String path = checkProperty(prop, "gen.target");
            entityDataVO.setTargetPath(path);
            entityDataVO.setPrintsql(Boolean.valueOf(prop.getProperty("gen.printsql")));
            entityDataVO.setReturnCls(checkProperty(prop,"gen.returnCls"));
            entityDataVO.setReturnSuccess(checkProperty(prop,"gen.return.success"));
            entityDataVO.setReturnFail(checkProperty(prop,"gen.return.fail"));
            entityDataVO.setBeanUtils(checkProperty(prop,"gen.bean.util"));
            entityDataVO.setBasePageDto(checkProperty(prop,"gen.pageDto"));
            entityDataVO.setReturnPageVo(checkProperty(prop,"gen.return.pagevo"));

            String dbType=checkProperty(prop, "db.type");
            entityDataVO.setDbType(dbType);
            String defaultDBName = checkProperty(prop, "db.defaultDBName");
            entityDataVO.setDefaultDBName(defaultDBName);
//            String prefix = checkProperty(prop, "db.prefix");
            String prefix= prop.getProperty("db.prefix");
            if (!prefix.isEmpty()){
                entityDataVO.setPrefix("\""+prefix+"\".");
            }else {
                entityDataVO.setPrefix("");
            }

            String host = checkProperty(prop, "db.host");
            String port = checkProperty(prop, "db.port");
            String tableNames = checkProperty(prop, "db.tableNames");
            String username = checkProperty(prop, "db.username");
            String password = checkProperty(prop, "db.password");
            StringBuffer dbUrlBuf=new StringBuffer();
            if (dbType.equals("mysql")){
                dbUrlBuf.append("jdbc:"+dbType+"://").append(host).append(":").append(port);
                dbUrlBuf.append("/").append(defaultDBName);
                dbUrlBuf.append("?user=").append(username).append("&password=").append(password);
                dbUrlBuf.append("&useSSL=false&serverTimezone=UTC");
                dbUrlBuf.append("&useUnicode=true&characterEncoding=UTF8");
            }else if(dbType.equals("postgresql")){
                dbUrlBuf.append("jdbc:"+dbType+"://").append(host).append(":").append(port);
                dbUrlBuf.append("/").append(defaultDBName);
                dbUrlBuf.append("?user=").append(username).append("&password=").append(password);
                dbUrlBuf.append("&useSSL=false&serverTimezone=UTC");
                dbUrlBuf.append("&useUnicode=true&characterEncoding=UTF8");
            }else {
                dbUrlBuf.append("jdbc:"+dbType+"://").append(host).append(":").append(port);
                dbUrlBuf.append(";DatabaseName=").append(defaultDBName);
                dbUrlBuf.append(";user=").append(username).append(";password=").append(password);
            }
//            "jdbc:sqlserver://localhost:1433;DatabaseName=student"
//            String dbUrl="jdbc:mysql://10.98.14.95:3306/daily?user=test&password=test123&useUnicode=true&characterEncoding=UTF8";
            entityDataVO.setDbConnectUrl(dbUrlBuf.toString());
            entityDataVO.setTableName(tableNames);
        } catch (Exception e) {
            throw new Exception("配置文件有误请检查",e);
        }


    }

    /**
     * 出现必填属性为空抛出异常，不为空返回值
     * @param prop
     * @param propertyString
     * @return
     * @throws Exception
     */
    public static String checkProperty(Properties prop,String propertyString) throws Exception {
        String str= prop.getProperty(propertyString);
        if (str==null||str.equals("")){
            throw new Exception(propertyString+" cannot be null");
        }else {
            return str;
        }
    }
}
